package com.cly.handler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.cly.base.contants.CacheKeysConstants;
import com.cly.base.contants.SocketConstants;
import com.cly.entity.Message;
import com.cly.enums.MessageType;
import com.cly.result.JsonRequest;
import com.cly.utils.RedisClientsUtils;
import com.cly.utils.SemaphoreUtils;
import com.cly.utils.WebSocketUtils;
import com.cly.vo.OnlineUserVo;
import com.cly.vo.UserVo;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.concurrent.Semaphore;

/**
 * WebSocket 处理器
 *
 * @author by 春风能解释
 * <p>
 * 2025/2/14
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class WebSocketHandler extends TextWebSocketHandler {

    /**
     * 信号量
     */
    private static final Semaphore socketSemaphore = new Semaphore(SocketConstants.SOCKET_MAX_ONLINE_COUNT);
    /**
     * redis 客户端
     */
    private final RedisClientsUtils redisClientsUtils;

    /**
     * 连接建立时
     *
     * @param session websocket 会话
     */
    @Override
    @SneakyThrows
    public void afterConnectionEstablished(WebSocketSession session) {
        boolean tryAcquire = SemaphoreUtils.tryAcquire(socketSemaphore);
        if (!tryAcquire) {
            // 获取不到信号量则拒绝连接
            WebSocketUtils.sendMessageToAllUsers(new Message()
                    .setType(MessageType.USER_LOGIN.getValue())
                    .setContent("当前在线人数超过限制数：" + SocketConstants.SOCKET_MAX_ONLINE_COUNT));
            session.close();
            return;
        }
        UserVo user = getUserInfo(session);
        WebSocketUtils.put(user.getId().toString(), session, new OnlineUserVo()
                .setUserId(user.getId().toString())
                .setUserName(user.getUsername())
                .setAvatarId(user.getAvatar().toString()));
        log.info("\n 建立连接 - {}", session);
        WebSocketUtils.sendOnlineUsers();
    }

    /**
     * 获取用户信息
     *
     * @param session websocket 会话
     * @return 用户信息
     */
    private UserVo getUserInfo(WebSocketSession session) {
        // 获取用户ID
        String json = redisClientsUtils.get(CacheKeysConstants.UserKeys.USER_LOGIN + "::" + session.getAcceptedProtocol());
        return JSON.parseObject(json, new TypeReference<JsonRequest<UserVo>>() {
        }).getData();
    }

    /**
     * 接收到消息
     *
     * @param session websocket 会话
     * @param message 文本型消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        WebSocketUtils.sendMessageToUser(session, message.getPayload());
    }

    /**
     * 发生错误
     *
     * @param session   websocket 会话
     * @param exception 异常
     */
    @SneakyThrows
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        if (session.isOpen()) {
            // 关闭连接
            session.close();
        }
        UserVo userInfo = getUserInfo(session);
        log.info("\n 连接异常 - {}\n 异常信息 - {}", userInfo.getId(), exception.getLocalizedMessage());
        // 移出用户
        WebSocketUtils.remove(userInfo.getId().toString());
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 连接关闭时
     *
     * @param session websocket 会话
     * @param status  关闭状态
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        UserVo userInfo = getUserInfo(session);
        // 移除用户
        WebSocketUtils.remove(userInfo.getId().toString());
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
        log.info("\n 断开连接 - {}", session);
        WebSocketUtils.sendOnlineUsers();
    }
}
